You have to think about a number of things when it
comes to services and security. Services themselves have to run in a
security context that has permissions to do what you program the
service to do. Imagine that you are programing the service to read a
file from the hard disk, read a setting from the registry, or even log
on to a Windows Authentication–only SQL Server. If the account that
your services runs under does not have the appropriate privileges, your
service will fail to perform the tasks you assigned it, and although
the service itself may actually run, it will be useless.
Running
a service in a higher privileged context than is required can cause
security holes, which provide opportunities for unapproved network
access, application access, and data loss. Never grant more authority
than your service requires to perform the tasks required of it.
You
should always determine what your service needs to do before you begin
to code it. Then, based on these needs, you determine the required
security rights. To help determine what options you have in terms of
security, let’s look at the default settings you have to choose from
when building a service.
Service Account Security
When you build a service, you have to select an account that the service will run under. This is considered the security context,
because the account (context) that your service runs under is not
necessarily the same as the user who may be logged on at the time.
For
example, if you open up the Services Control Panel and look at the Log
On As column, you will see many different listings. Let’s look at what
the choices are when we install a service.
Let’s open up the Services Control Panel and right-click the Tutorials service. Select Properties and then click the Log On tab.
Local System
You
have two options: to run as a Local System Account or to run as a
specific user account. For the moment ignore the option Allow Service
To Interact With Desktop—I’ll cover that in a moment. Most services use
some predefined system accounts that are created when the operating
system is installed. Although these accounts are technically user
accounts, they are predefined with specific security attributes,
privileges, and default security response types when queried by remote
computers. In most cases you should use one of the predefined
NetworkService or LocalService accounts.
The
Local System account itself has access to most resources on the local
computer that it exists on. It has no password and is not considered a
real user. It is an account created for use by the service control
manager (SCM) under whose context many services run. However, this
account has quite extensive privileges, and any service running under
this account has those privileges. For this reason it is important to
avoid using this account unless necessary. You can and should create an
account with the minimum privileges required to perform the service’s
duties.
Note one
important thing about the Local System account: Although it has a large
amount of privileges when running on the local computer, it has almost
no privileges when attempting to access network resources. For this
reason, the Local System requires null sessions—sessions with only anonymous authentication (in other words, no authentication).
You
can restrict null sessions on computers throughout your network,
protecting them from services running on remote computers. If you plan
to access remote resources, it’s better in many situations to use the
local computer’s Machine Account and then grant that account access to
the remote resources.
User Accounts
You
can create, define, and give any user account the required specific
privileges and security rights—such as if you wanted the service to
query data from a database on a computer running Microsoft SQL Server
that requires a valid Windows account. You can set the service to run
as a user who has Windows Authentication to Microsoft SQL
Server—whether a local account or a domain account—and then when the
service queries SQL Server, it will send the credentials as if it were
that. Let’s look at the options:
User
This option causes the system to prompt for a valid user name and
password when the service is installed and runs in the context of an
account specified by a single user on the network. This is the most
powerful of all security contexts because you define what access the
account has when the account is created. If you run the service as a
domain enterprise administrator, the service will have the privileges
of that domain administrator. You need to define the exact privileges
that are required by a service to determine whether this is a viable
and reasonable option. You’d be hard-pressed to find a situation that
would require such extreme privileges. Under Windows 2000, Windows
Server 2003, and Windows Vista, you are best off assigning your service
as a Network Service
account or a Machine account. After doing so, you can grant privileges
to other computers, or services such as Microsoft MSMQ, by adding the
machine account that the service runs under to the remote resource.
Local Service
A service running as a Local Service runs in the context of a
reduced-privilege user on the local computer and presents anonymous
credentials to any remote server. This option is appropriate for
situations that require very limited security and that do not require
access to remote network resources. Because the service would only
access remote resources using null sessions with anonymous access, it
is not recommended that you use it for network resource access.
Null
sessions (anonymous logons) pass no user name and no password to the
local or remote resource for authentication. If the remote system has
blocked null sessions or anonymous logon, you won’t be able to access
the required resource. In this case you should consider using the
Network Service account.
Local System
This option runs in the context of an account that provides extensive
local privileges and presents the computer’s credentials to any remote
server. This is a very powerful account to use and will allow you to
perform almost any action. You must be careful when using Local System
as your security context because allowing a service to run as Local
System grants more privileges than most services require. Consider this
option only when requiring privileges equivalent to those of an
administrator. In most cases, you should consider the more restrictive
options and then extend the default privileges of those accounts,
instead of granting the service more privileges under a Local System.
If the service does not require elevated or interactive privileges,
consider using Local Service or Network Service accounts instead.
Network Service
A service running as a Network Service runs in the context of a
non-privileged user on the local computer and presents the computer’s
credentials to any remote server. This account is much more powerful
than Local Service, but is still much more secure than Local System,
especially when it comes to remote computers who query this service.
External services running in the context of the Everyone and
Authenticated Users groups merely receive anonymous credentials that
they would not be able to use to spoof the computer credentials given
by Local System.
What
is spoofing? When services run in a security context higher than is
required, they run the risk of external users attempting to mimic, or spoof,
the credentials of the service itself. In this case, a malicious user
may intend to use executables, batch command files, or other executable
processes by exploiting the capabilities of your services. Once these
processes are running, they have the same privileges as your service
and therefore can spoof the service credentials and force the service
to perform tasks it was not intended to perform—against other services
or resources it was not granted explicit access to.
You
may not always be clear about what you initially need, and sometimes
you may need to rely on trial and error. If you are unsure, talk with
your network administrator and work out how to create a user account
that can be managed by your administrator so that it can be limited
to doing only what it needs to do. In instances in which a service may
need to run on a large number of computers and does not require special
privileges or network access, consider using the LocalService or
NetworkService accounts. If the service requires a lot of network
access or privileges, a secured domain account is often the best option.
When
deciding on the service account you want to use, the level of of
security should be paramount. The choices can be considered in the
following order, from most to least secure:
Securing the Service
It
is very important to think about not just what your service will do, or
what security context it needs to run in, but how to protect your
service and your business from unauthorized usage of your service and
its functionality.
Imagine
that you have a service that can download important customer
information or internal employee information, sales figures, or other
confidential information. In these cases you probably want to keep
access to this service and its functionality secure. You want to limit
the access that the service has.
Protecting Data
Let’s
say you design a service that has the ability to download all customer
and credit card information from one data store and then process the
data to store into another data store. What do you need to secure?
Access to the data store that holds the customer information
Access to the directory to which the service downloads the information before processing
Access to the secondary data store that will hold the customer information
Access to temporary data and data connections
Each
type of access has particular issues that must be addressed. However,
as with all applications or services, each type of access has different
types of functionality. Therefore you should always look at your
service and determine what actions it takes, how you will perform them,
and which of those actions need to be secured. That last
step—determining how to secure them—is
important. Local development resources at your company, on the
Internet, and from local administrators can often help you with any of
these steps.
Access to the Data Store That Holds the Customer Information
Normally
this type of data is stored in a back-end database. Database servers
that run on the Windows platform usually support NTLM and Kerberos-type
authentication, as well as SQL Server authentication, using built-in
logons. In this situation we have to figure out how to allow the
service access to the data without providing too much access or access
that can be abused by someone with malicious intent.
As
I’ll explain in the following sections, you can have the service run in
the context of a user who has access to the database, or you can supply
the service with the security context or logon information with which
it can impersonate the user’s security access. You can also specify
security logon information in the connection string used to connect to
the database.
As a User
If
you decide to run the service as a user with security rights, make sure
that the user account has only the privileges required to retrieve the
data and to process it locally, especially if you plan to store the
data temporarily on the local disk.
Impersonation
If
you decide to supply the service with the security credentials for
either a Windows account or a SQL Server logon, you have to decide how
to get this information to the service while keeping it safe. It does
no good to secure the use of your service if you make it easy to obtain
the logon credentials that provide access to the confidential
information.
You
can place the user name and password in an encrypted file or encrypted
registry key, or you can use a secondary Web Service or COM+
application that has permission to access the database on behalf of the
service. Both are good ways to provide access to the data while
securing logon information.
Access to the Directory to Which the Service Downloads the Information Before Processing
It
is possible to process the data in two formats. The first format is
memory only, where you might query the information and then store it in
local memory resident ADO.NET datasets, or other memory-mapped data
objects that are then processed directly to the secondary data store.
The second option is to save the data temporarily to the file system,
where it is either processed by the same service—possibly on a separate
thread—or by another service.
Although
not a direct concern for the developer, from an operational support and
security point of view, if the data is stored in memory, your
operations team should keep track of access to the server where the
data is being processed. Depending on the data itself, you might not be
able to encrypt the memory used to store the data while it is being
processed. This means you must be diligent about local server access.
When
determining whether you should store data in temporary local files, you
should look at some possible solutions for helping to ensure the safety
of your data:
Encrypt the data as it is saved to disk.
Store temporary files in encrypted folders accessible only by the service account.
Process the file in memory and not create an unencrypted version on disk.
Access to the Secondary Data Store That Will Hold the Customer Information
At
times you will be pulling data from one database or data store and you
might want to migrate it, or convert it to another format and then
store it in a secondary database or data store. In this case you will
need to review the security model and approach that you use.
Imagine
that you are running the service as a local user account or even as a
domain account or machine account. The service itself might have access
to the data store that you retrieve from, but not have access to the
back end. This could be due to trust issues in the domain, multi-homed
servers, or any other security or configuration requirement on your
network or business. If this is the case, your service could be sharing
the data between two components, instantiated by the service but
running under two different security contexts. In this case the service
is running under one account and so is child process 1 (or thread 1)
and the second thread needs to run as a secondary account. In this case
you are passing data through the service itself between two threads or
two components created by the service, but running under different
security contexts. This following list, while not exhaustive, gives you
some options for possible solutions to this problem:
Create matching SQL logons on both servers and then share this data between both connections.
Use
Windows authentication by running the service under an account that has
access to both data stores. Use a SQL logon for one data store and use
the security context of the service for the other.
Use
multiple user security contexts on a per-thread basis and share the
data between the threads. This option requires that you inherent from
existing security contexts, create new secuirty contexts that you then
pass onto a new thread, or that you have the thread impersonate a
security context that has the required access. Although there aren’t
many situations in which you would run a service in such a model,
threading and services are not limited to a single security context.
Microsoft provides APIs and .NET methods for creating such threads.
Use COM+ services or Web Services to perform the data retrieval and storage.
You
can perform data retrieval from different data sources in many
different ways. What’s important is to make sure that you don’t give
the service too much authorization, while at the same time protecting
the logon information so that data on both servers is protected.
Access to the Temporary Data and Data Connections
Not
only do you need to make sure the data is protected while you are
processing it, but you also need to make sure that the data is secured
once it has been processed. Make sure that you clear any data and
memory buffers, close any unused connections and recordsets, and most
important, make sure to destroy any locally stored data.